﻿using System;   
using System.Collections.Generic;
using System.Reflection;

namespace Eksponent.RazorRocks.Helpers
{
    public static class CollectionHelpers
    {
        /// <summary>
        /// Determines whether the array is null or empty
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="array">The array.</param>
        /// <returns>
        ///   <c>true</c> if the array is null or empty; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsNullOrEmpty<T>(this T[] array)
        {
            return array == null || array.Length == 0;
        }


        /// <summary>
        /// Converts a single object into a list containing that.
        /// </summary>
        /// <typeparam name="TElement"></typeparam>
        /// <param name="element">The element for the list.</param>
        /// <returns></returns>
        public static List<TElement> WrapList<TElement>(this TElement element)
        {
            return new List<TElement> { element };
        }



        /// <summary>
        /// Gets the value with the specified key from the dictionary or returns returns a default value if it's missing
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="dict">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <param name="defaultValue">A function that returns the default value. If null default(TValue) is used (e.g. null for classes)</param>
        /// <returns></returns>
        public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key,
                                                        Func<TValue> defaultValue = null)
        {
            TValue val;
            if (dict.TryGetValue(key, out val))
            {
                return val;
            }
            return defaultValue != null ? defaultValue() : default(TValue);
        }


        /// <summary>
        /// Gets the value with the specified key from the dictionary or initializes it with the function specified.
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="dict">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <param name="initializer">The initializer.</param>
        /// <param name="set">if set to <c>true</c> the value from the initalizer is added to the dictionary if it doesn't contain the key.</param>
        /// <returns></returns>
        public static TValue GetOrInitialize<TKey, TValue>(this IDictionary<TKey, TValue> dict,
                                                           TKey key,
                                                           Func<TValue> initializer,
                                                           bool set = true)
        {
            TValue val;
            if (!dict.TryGetValue(key, out val))
            {
                val = initializer();
                if (set)
                {
                    //Set in this way to make it thread safe(ish)
                    dict[key] = val;
                }
            }
            return val;
        }

        /// <summary>
        /// Gets the value with the specified key from the dictionary or initializes it with the value type's empty constructor
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="dict">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <param name="set">if set to <c>true</c> the value from the initalizer is added to the dictionary if it doesn't contain the key.</param>
        /// <returns></returns>
        public static TValue GetOrInitialize<TKey, TValue>(this IDictionary<TKey, TValue> dict,
                                                           TKey key, bool set = true) where TValue : new()
        {
            return dict.GetOrInitialize(key, () => new TValue(), set);
        }


        /// <summary>
        /// Converts an anonymous type (i.e. parameter object) to a dictionary.
        /// If the object is already a dictionary this is returned.
        /// </summary>
        /// <param name="anonymousTypeOrDictionary">The object to convert.</param>
        /// <returns>A dictionary with the parameters</returns>
        public static IDictionary<string, object> AsDictionary(this object anonymousTypeOrDictionary)
        {
            return AsDictionary<Dictionary<string, object>>(anonymousTypeOrDictionary);
        }

        public static TDictionary AsDictionary<TDictionary>(this object anonymousTypeOrDictionary)
            where TDictionary : IDictionary<string, object>, new()
        {
            if (anonymousTypeOrDictionary == null)
            {
                return new TDictionary();
            }
            
            if (anonymousTypeOrDictionary is TDictionary)
            {
                return (TDictionary) anonymousTypeOrDictionary;
            }

            var parameters = new TDictionary();
            var attr = BindingFlags.Public | BindingFlags.Instance;
            foreach (var property in anonymousTypeOrDictionary.GetType().GetProperties(attr))
            {
                if (property.CanRead)
                {
                    parameters.Add(property.Name,
                                   property.GetValue(anonymousTypeOrDictionary, null));
                }
            }

            return parameters;
        }        
    }
}